#include "AsyncModel.h"
#include "StringUtil.h"

static void async_callback(uv_async_t* handle) {
  AsyncModel *model = reinterpret_cast<AsyncModel*>(handle->data);
  model->Callback();
  model->Wake();
}

static void close_callback(uv_handle_t* handle) {
  delete handle;
}

AsyncModel::AsyncModel(std::function<void(JsonObject::Ptr)> invoke) :
  extend_data_(NULL),
  async_(NULL),
  callback_(NULL),
  invoke_(invoke) {
  event_ = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  data_ = JsonObject::CreateJsonObject();
}

AsyncModel::AsyncModel(Nan::Callback* callback) :
  extend_data_(NULL),
  async_(NULL),
  callback_(callback),
  invoke_(NULL) {
  event_ = ::CreateEvent(NULL, FALSE, FALSE, NULL);
  data_ = JsonObject::CreateJsonObject();
}

AsyncModel::~AsyncModel() {
  ::CloseHandle(event_);
}

void AsyncModel::SetExtendData(void* extend_data) {
  extend_data_ = extend_data;
}

void* AsyncModel::GetExtendData() {
  return extend_data_;
}

JsonObject::Ptr AsyncModel::GetData() {
  return data_;
}

int AsyncModel::Send() {
  async_ = new uv_async_t;
  async_->data = this;
  uv_async_init(uv_default_loop(), async_, async_callback);
  return uv_async_send(async_);
}

void AsyncModel::Callback() {
  if (callback_ != NULL && !callback_->IsEmpty()) {
    Nan::HandleScope scope;

    const int argc = 1;
    v8::Local<v8::Value> result;
    data_->ToV8Value(result);
    v8::Local<v8::Value> argv[argc] = { result };
    Nan::AsyncResource resource("AsyncModel");
    callback_->Call(argc, argv, &resource);
  }

  if (invoke_ != NULL) {
    invoke_(data_);
  }
  uv_close((uv_handle_t*)async_, close_callback);
}

void AsyncModel::Wake() {
  ::SetEvent(event_);
}

void AsyncModel::Wait() {
  ::WaitForSingleObject(event_, INFINITE);
}
